"""
Create a browser for navigation.
"""
import os
import threading
import random
import tempfile
import shutil
import time
from subprocess import *
from Queue import Queue

from browser.utils import get_host
from browser.proxy import ThreadingHTTPServer, ProxyHandler
from browser.check_page import check_page

__all__ = ('Firefox',)

FIREFOX_PATH = '/usr/lib/firefox/firefox-2-bin'
null = open('/dev/null', 'wb')


class BrowserError(Exception):
    pass

class Browser(object):
    def __init__(self, display, start_vnc=True):
        self.display = display
        self.profile_dir = None
        self.browser_process = None
        self.vnc_process = None
        if start_vnc:
            self._start_vnc()
        self.server_thread = ServerThread(self._get_port())
        self.server_thread.start()

    def start_browser(self):
        raise NotImplementedError()

    def open_url(self, url, timeout=60):
        if not self.browser_process:
            self.start_browser()

        if not url.startswith('http'):
            url = 'http://' + url
        new_url = check_page(url)
        if not new_url:
            while not self.server_thread.server.queue.empty():
                try:
                    self.server_thread.server.queue.get_nowait()
                except:
                    pass
            raise BrowserError("URL Fails simple tests")
        self.server_thread.server.finished = False
        self.run_command("openTop",
                         "http://%s/chitika-rc/RemoteRunner.html?cache_buster=%s" % 
                         (get_host(new_url), int(1000000 * random.random())))

        self.run_command("open", new_url)
        self.run_command("open", new_url, 'finish')

        start_time = time.time()
        while time.time() - start_time < timeout:
            if self.server_thread.server.finished:
                break
            time.sleep(0.05)
        else:
            while not self.server_thread.server.queue.empty():
                try:
                    self.server_thread.server.queue.get_nowait()
                except:
                    pass
            self.server_thread.server.queue = Queue()
            self._cycle()
            raise BrowserError("URL Timed Out")
        while not self.server_thread.server.queue.empty():
            try:
                self.server_thread.server.queue.get_nowait()
            except:
                pass
        time.sleep(5)


    def run_command(self, cmd, *args):
        self.server_thread.server.queue.put((cmd,) + args)

    def construct_profile(self):
        raise NotImplementedError()

    def _cycle(self):
        self.clean_up(kill_vnc=False)
        self.start_browser()

    def clean_up(self, kill_vnc=True):
        if self.browser_process:
            try:
                os.kill(self.browser_process.pid, 7)
                self.browser_process.wait()
            except:
                pass
            self.browser_process = None
        if self.profile_dir:
            try:
                shutil.rmtree(self.profile_dir)
            except:
                pass
            self.profile_dir = None
        if self.vnc_process and kill_vnc:
            try:
                os.kill(self.vnc_process.pid, 7)
                self.vnc_process.wait()
            except:
                pass
            self.vnc_process = None

    def _get_port(self):
        return 8000 + int(self.display)

    def _get_start_url(self):
        return 'http://remote_test/chitika-rc/RemoteRunner.html?cache_buster=%d' % int(1000000 * random.random())

    def _get_home(self):
        home = os.path.join(os.environ['HOME'], str(self.display))
        try:
            os.makedirs(home)
        except:
            pass
        return home

    def _start_vnc(self):
        if self.vnc_process:
            self.clean_up()

        start_args = ('vncserver', ":%s" % self.display,
                      '-name', 'screenshot', '-depth', '24', '-geometry', '1024x768')

        env = os.environ.copy()
        env['HOME'] = self._get_home()
        self.vnc_process = Popen(start_args,
                                 env=env)
        time.sleep(5)

    def _vnc_cpu_usage(self):
        ps_args = tuple('ps up 10160 --no-heading'.split())
        ps = Popen(ps_args, stderr=null, stdout=PIPE)
        output = ps.stdout.read().strip()
        try:
            cpu = float(output.split()[2])
        except:
            return
        if cpu > 20:
            sys.stderr.write("Warning: VNC CPU Usage is High\n")
        return cpu

    def __del__(self):
        self.clean_up()




class Firefox(Browser):
    def __init__(self, display, start_vnc=True, firefox_path=FIREFOX_PATH):
        self._remove_old_firefox(display)
        super(Firefox, self).__init__(display, start_vnc)
        self.firefox_path = firefox_path


    def _remove_old_firefox(self, display):
        ps_output = Popen('ps aux', shell=True, stdout=PIPE).communicate()[0]

        display_str = 'display :%s' % display

        for line in ps_output.split('\n'):
            if 'firefox' in line.lower() and display_str in line.lower():
                try:
                    pid = int(line.split()[1])
                    os.kill(pid, 7)
                except:
                    pass

    def construct_profile(self):
        if self.profile_dir and os.path.exists(self.profile_dir):
            return self.profile_dir

        original_profile = os.path.join(os.path.dirname(__file__),
                                        'firefox', 'tmp_profile')
        profile_dir = tempfile.mkdtemp(suffix = 'profile')
        try:
            shutil.rmtree(profile_dir)
        except OSError:
            pass
        shutil.copytree(original_profile, profile_dir)

        prefs = open(os.path.join(profile_dir, 'prefs.js'), 'a')
        prefs.write("""user_pref("network.proxy.http_port", %d);\n""" % 
                    self._get_port())
        prefs.close()
        self.profile_dir = profile_dir
        return profile_dir

    def start_browser(self):
        if self.browser_process:
            self.clean_up()
        self.browser_process = Popen(self.get_firefox_args(),
                                     env = self.get_firefox_env(),
                                     stderr = null,
                                     stdout = null)
        time.sleep(7)

    def get_firefox_args(self):
        return (
            self.firefox_path,
            '--display', ":%s" % self.display,
            '-profile', self.construct_profile(),
            self._get_start_url(),
            '-width', '1024', '-height', '768',
            )

    def get_firefox_env(self):
        env = os.environ.copy()
        env['HOME'] = self._get_home()
        env['DISPLAY'] = ":%s" % self.display
        env['MOZ_NO_REMOTE'] = '1'
        return env    



class ServerThread(threading.Thread):
    def __init__(self, port):
        threading.Thread.__init__(self, name="rc_server_thread")
        self.port = port
        self.setDaemon(True)

    def run(self):
        server_address = ('127.0.0.1', self.port)
        httpd = ThreadingHTTPServer(server_address, ProxyHandler)
        httpd.queue = Queue()
        self.server = httpd
        httpd.serve_forever()
